function countingSort(arr) {
  var len = arr.length,
    Result = [],
    Count = [],
    min = max = arr[0]; // 刚开始初始化为0
  /*查找最大最小值,并将arr数置入Count数组中,统计出现次数*/
  for (var i = 0; i < len; i++) {
    Count[arr[i]] = Count[arr[i]] ? Count[arr[i]] + 1 : 1;
    min = min <= arr[i] ? min : arr[i];
    max = max >= arr[i] ? max : arr[i];
  }
  /*从最小值->最大值,将计数逐项相加*/
  for (var j = min; j < max; j++) {
    Count[j + 1] = (Count[j + 1] || 0) + (Count[j] || 0);
  }
  /*Count中,下标为arr数值,数据为arr数值出现次数;反向填充数据进入Result数据*/
  for (var k = len - 1; k >= 0; k--) {
    /*Result[位置] = arr数据*/
    Result[Count[arr[k]] - 1] = arr[k];
    /*减少Count数组中保存的计数*/
    Count[arr[k]]--;
    /*显示Result数组每一步详情*/
  }
  return Result
}

const arr = [2, 1, 10, 8, 5, 4, 3, 9, 6, 0, -1, -2, 2 * 1, 3 * -1, 1 - 5, 7, 1.1, 1.01]
console.log(countingSort(arr));


// ** 计数排序 计数排序属于桶子法，当桶的个数最大的时候(间隔为1)，就是计数排序。
// ** 先讲桶排序:
// 将待排序元素划分到不同的痛。先扫描一遍序列求出最大值 maxV 和最小值 minV 
// 设桶的个数为 k ，则把区间[minV, maxV] 均匀划分成 k 个区间
// 每个区间就是一个桶。将序列中的元素分配到各自的桶。
// 对每个桶内的元素进行排序。可以选择任意一种排序算法。
// 将各个桶中的元素合并成一个大的有序序列。
// 桶越多，时间效率就越高，而桶越多，空间就越大。
// 计数排序本质上是一种特殊的桶排序，当桶的个数最大的时候，就是计数排序。
// ** 牺牲空间来保证时间

// ** 注意点:
// ! 可以排负数，但浮点数不行    
// ! 原因就在于普遍的桶排序间隔最小是1，也就是计数排序，当然你分更小的桶理论上是可以实现，但精度需要兼容